from __future__ import generators
import os,random,sets,sys,socket,cStringIO
#from BitVector import BitVector
from pprint import pprint
import posnet_overlay_messages
import array
import struct

def set_reuse_addr (sock):
	""" from asyncore.py"""
	# try to re-use a server port if possible
	try:
		sock.setsockopt (
			socket.SOL_SOCKET, socket.SO_REUSEADDR,
			sock.getsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1
			)
	except:
		pass


#-------------------------------------------------------
# Send messages to a PosNET process waiting for answers
#-------------------------------------------------------
def send_answer_message( host,port_number,m,source = None):
    try:
        mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
        mySocket.connect( (host,int(port_number)) )
        if source is not None: m.source = source
        send_posnet_message_over_tcp_socket(m,mySocket,True)
        mySocket.close()
    except Exception,e:
        print >>sys.stderr,"Error: host=",host,port_number
        sys.stderr.flush()
        raise

def send_answer_end(host,port_number,source,query_id = None):
    mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
    target = (host,int(port_number))
    mySocket.connect(  target )
    message = posnet_overlay_messages.posnet_finish_send_results(source,target,query_id)
    send_posnet_message_over_tcp_socket(message,mySocket,True)
    mySocket.close()

#---------------------------------------------------
# PosNET process to receive answers
#---------------------------------------------------
def recv_answers_prepare(port_number = None,timeout= None):
    """ receive answers on a socket listening for the specified port number
        @param port_number: used to listen to answers
        @param timeout: timeout before an error is raised
    """
    if timeout is not None: timeout = int(timeout)
    else: timeout = 30
    mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
    #TODO ! mySocket.settimeout(timeout)
    if port_number is None: port_number = 0
    port_number_integer = int(port_number)
    #mywaitinghostname = socket.INADDR_ANY
    mywaitinghostname = ""
    try:
        set_reuse_addr(mySocket)
        mySocket.bind ( ( mywaitinghostname, port_number_integer) )
        backlog_param = 5 # backlog param specifies number of queued connections : should be 1 or 5...
        mySocket.listen ( backlog_param )
        return mySocket
    except Exception,e:
        print >>sys.stderr,"Error while binding",( mywaitinghostname, port_number_integer),e
        raise
    
def recv_answers_iter_wait(mySocket,waiting_channel= None,logger = None):
    """ receive answers on a socket listening for the specified port number
        @param mySocket: listening socket
        @param waiting_channel: a set or a table of channels to wait
    """
    wait  = sets.Set()
    if waiting_channel is not None:
        for x in waiting_channel: wait.add(x)
    # Debug
    #####if logger is not None: logger.info("---------------- begin work on"+str(mySocket.getsockname()))
    #####else: print  >>sys.stderr,"---------------- begin work on",mySocket.getsockname()
    #--------
    while True:
        # Debug
        #####if logger is not None: logger.info("recv_answers_iter: waiting on "+str(mySocket.getsockname())+", wait="+str(wait))
        #####else: print  >>sys.stderr,"recv_answers_iter: waiting on",mySocket.getsockname(),",wait=",wait
        #-------
        channel, sender = mySocket.accept()
        # Debug
        #####if logger is not None: logger.info("recv_answers_iter: incoming connection from " +str(sender)+" socket="+str(channel.getsockname())+" <- "+str(channel.getpeername()))
        #####else:  print  >>sys.stderr,"recv_answers_iter: incoming connection from",sender
        #-------
        msg = recv_posnet_message_over_tcp_socket(channel,False)
        if msg is None: pass
        else:
            if hasattr(msg.data,"next"):
                for next in msg.data.next:
                    oaddr = next[0]
                    query_id = next[1]
                    waddr = (oaddr,query_id)
                    if waddr not in wait:
                        wait.add(waddr)
                        # Debug
                        #if logger is not None: logger.info("recv_answers_iter: wait for="+str(waddr))
                        #else: print >>sys.stderr,"recv_answers_iter: wait for=",waddr
                        #------
            towait = msg.source
            if msg.message_type == posnet_overlay_messages.posnet_message_type_finish_send_results:
                if msg.data is not None and msg.data != "": towait = (msg.source,msg.data)
                if  towait in wait:
                    wait.remove(towait)
                    # Debug
                    #if logger is not None: logger.info("recv_answers_iter: end wait from"+str(towait))
                    #else: print >>sys.stderr,"recv_answers_iter: end wait from",towait
                    #-----
                else:
                    # Debug
                    #if logger is not None: logger.info("recv_answers_iter: end for sender but not found in waiting : "+str(towait) )
                    #else:
                    #    print >>sys.stderr,"recv_answers_iter: sender indicates end but was not found in waiting..."
                    #    print >>sys.stderr,"sender=",towait,"wait=",wait
                    #----
                    pass
                if len(wait) == 0: break
            else:
                if msg.message_type == posnet_overlay_messages.posnet_message_type_result:
                    towait = (msg.source,msg.data.query_id)
                if towait not in wait:
                    # Debug
                    #if logger is not None: logger.info("recv_answers_iter: record sender:" +str(towait))
                    #else: print >>sys.stderr,"recv_answers_iter: record sender:",towait
                    #---------
                    wait.add(towait)
                yield sender,msg
    #####if logger is not None: logger.info("---------------- end work on "+str(mySocket.getsockname()))
    #####else: print  >>sys.stderr,"---------------- end work on",mySocket.getsockname()
    sys.stderr.flush()
        
#----------------------------------------------------------
posnet_tcp_server_quit_data =  "quit"
def send_posnet_message_over_tcp_socket(message,sock,shut  = None,buf_size = None):
	""" pack a posnet message and send it over the tcp socket
	    @param message: posnet message
	    @param sock: tcp socket used to transport the data
	    @param shut: if True, it will close the writing of this socket after message being sent
	    @param buf_size: size of chunks send with socket.sendall() function
	"""
	if shut is None: shut = True
	if buf_size is None: buf_size = 1024 *1024
	###tosend =  posnet_overlay_messages.pack_overlay_message( message )
	atosend =  posnet_overlay_messages.overlay_message_to_array( message )
	###max = len(tosend)
	max = atosend.buffer_info()[1] * atosend.itemsize
	#######print "send",max,"bytes from ",message.source,"->",message.target,"msg_type=",message.message_type
	sys.stdout.flush()
	sock.sendall( array.array("c",  struct.pack("L",  max )).tostring() ) 	
	###sock.sendall( posnet_overlay_messages.pack_bitstring( str( BitVector( intVal = int(len(tosend)) , size = 32 )) ) )	
	sock.sendall( atosend.tostring() )
	##### OLD version
	if False:	
		current = 0
		while current < len(tosend):
			end = current+buf_size
			if end > max: end = max
			sock.sendall( tosend[current:end] )
			current = end
		#if shut: sock.shutdown(socket.SHUT_WR)

def recv_posnet_message_over_tcp_socket(sock,shut = None,buf_size = None):
	""" receive a posnet message over the tcp socket
	    @param sock: tcp socket used to transport the data
	    @param shut: if True, it will close the reading of this socket after message being received
	    @param buf_size: size of chunks send with socket.recv() function
	"""
	# TODO with array
	raw_message = array.array("c")
	if shut is None: shut = True
	if buf_size is None: buf_size = 1024 
	try:
		mydat = sock.recv(4)
		datasize  = array.array("L", mydat).pop()	
		###datasize = int(posnet_overlay_messages.unpack_bits(mydat))
		if datasize == 0: return None # empty message !
		while len(raw_message) < datasize:
			wait_size = buf_size
			if wait_size > datasize-len(raw_message): wait_size = datasize-len(raw_message) 
			data = sock.recv(wait_size)
			raw_message.extend( data )
			###raw_message += data       
		#if shut: sock.shutdown(socket.SHUT_RD)
		message = posnet_overlay_messages.array_to_overlay_message( raw_message )
		return message
	except:
		raise

def send_tcp_message(target,message, add_sock_name = None,wait_for_reply = None,logger = None):
	""" send a posnet message using tcp to a posnet overlay node
	    @param : (target_address, target_port) address and port of the posnet overlay node to contact, 
	    @param message: posnet message to send
	    @param add_sock_name: (default: False) if True, the posnet message will be modified before send: the source address and port is replaced in the posnet message.			
	    @param wait_for_reply: (default: False) if True, it will wait, after send the message, for a replied message
	"""
	target_address,target_port = target
	# Debug
	#####if logger is not None: logger.info("send_tcp_message: to "+str(target_address)+","+str(target_port)+" "+str(message.__class__.__name__))
	#####else: print "send_tcp_message: to",target_address,target_port,message.__class__.__name__
	#--------
	async = None
	if wait_for_reply:
		if message.answer_type == 0: async = False
		else: async = True	
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((target_address,target_port))	
		if (add_sock_name is not None and add_sock_name) or ( async is not None and async):
			message.source = s.getsockname()
		#---------------------------------
		my_receive_socket = None
		socket_port_number = None
		shut = None
		if message.answer_type != 0:
			shut = True
			my_receive_socket =  recv_answers_prepare()
			socket_port_number = my_receive_socket.getsockname()[1]
			message.answer_type = socket_port_number
		send_posnet_message_over_tcp_socket(message,s,shut)
		ret = None
		if wait_for_reply is not None and wait_for_reply:
			if message.answer_type == 0:
				debug_socket_name = s.getsockname()
				# Debug
				#if logger is not None: logger.info("send_tcp_message: receive answer back in the socket " +str(debug_socket_name))
				#else: print "send_tcp_message: receive answer back in the socket",debug_socket_name
				#------
				ret = None
				try: ret = recv_posnet_message_over_tcp_socket(s)
				except:
					pass #no answer :)
				# Debug
				#if logger is not None: logger.info("send_tcp_message: answer received "+str(debug_socket_name))
				#else: print "send_tcp_message: answer received",debug_socket_name
				#----------
			s.close()
			if message.answer_type != 0:
				ret = []
				# Debug
				#if logger is not None: logger.info("send_tcp_message: wait for answers on listening socket port "+str(socket_port_number))
				#else: print "send_tcp_message: wait for answers on listening socket port",socket_port_number
				#-----------
				for sender,msg in recv_answers_iter_wait(my_receive_socket,None,logger):
					ret.append(msg)
					# Debug
					msg_str = str(None)
					if msg is not None: msg_str = msg.__class__.__name__
					#if logger is not None: logger.info("send_tcp_message: message received from "+str(sender)+" : "+str(msg_str))
					#else: print "send_tcp_message: message received from",sender,":",msg_str
					#-------------
		else: s.close()
		return ret
	except Exception, e:
		from traceback import print_exc
		print "Error in send_tcp_message(",target_address,target_port,")"
		print_exc()
		raise

def send_quit_tcp_message(target_address,target_port):
	""" send a tcp message to a posnet overlay node telling its tcp server loop to exit
	    WARNING: this id a dirty way to kill a posnet overlay node: USE IT FOR DEBUG ONLY  

	    @param target_address: address of the posnet overlay node to contact
	    @param target_port: port of the posnet node to contact
	"""
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((target_address,target_port))	
	global posnet_tcp_server_quit_data
	s.sendall(posnet_tcp_server_quit_data)
	s.shutdown(socket.SHUT_WR)
	s.close()
	

def send_message(destination,msg,answer_type = None,logger =None):
    if answer_type is None: answer_type = 0
    msg.answer_type = answer_type
    if answer_type == 0:
        # Debug
        #####if logger is not None: logger.info("send_message: send message to "+str(destination))
        #####else: print "send_message: send message to",destination
        #-----
        ack_message = send_tcp_message(destination,msg,True,True,logger)
        if ack_message is not None:
            # Debug
            #####if logger is not None: logger.info("send_message: answer received on "+str(ack_message.target)+" from "+str(ack_message.source)+ " : "+str(ack_message.__class__.__name__))
            #####else: print "send_message: answer received on",ack_message.target,"from",ack_message.source,":",ack_message.__class__.__name__
            #------------
            return ack_message.data
        return None
    else:
        # Debug
        #####if logger is not None: logger.info("send_message -> send message to "+str(destination))
        #####else: print "send_message -> send message to",destination
        #-------
        ack_messages = send_tcp_message(destination,msg,True,True,logger)
        ack_message = ack_messages[0]
        # Debug
        #####if logger is not None: logger.info("send_message <- answer received from "+str(ack_message.target)+" from "+str(ack_message.source) + " : "+str(ack_message.__class__.__name__))
        #####else: print "send_message <- answer received from",ack_message.target,"from",ack_message.source,":",ack_message.__class__.__name__
        #-----
        #print len( ack_message.data )/1024.0,"Kbytes of data from",ack_message.source
        return ack_message.data

def send_query_message(destination,msg,logger = None):
    msg.answer_type = -1
    ack_messages = send_tcp_message(destination,msg,True,True,logger)
    # Debug
    for ack_message in ack_messages:
        ack_message_str  = str(None)
        if ack_message is not None: ack_message_str = ack_message.__class__.__name__
        #####if logger is not None:  logger.info("send_query_message: answer received on "+str(ack_message.target)+" from "+str(ack_message.source)+" : "+str(ack_message_str))
        #####else: print "send_query_message: answer received on",ack_message.target,"from",ack_message.source,":",ack_message_str
    #-------
    return ack_messages

#-----------------------------------------
# Implementation of client functions
#-----------------------------------------

def posnet_ping(destination,answer_type = None):
	""" send a posnet ping message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	    @param answer_type: type of answer 
	"""
	m  = posnet_overlay_messages.posnet_ping_message( (None,None), destination ,None)
	print "ping posnet overlay node",destination
	sys.stdout.flush() 
	return send_message(destination,m,answer_type)

def posnet_draw(destination,other = None):
	""" send a posnet ping message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	    @param other: optional to request other nodes	
	"""
	if other is None or not other: other = False
	else: other = True 	
	m  = posnet_overlay_messages.posnet_draw_message( (None,None), destination, other)
	print "draw posnet overlay node",destination
	sys.stdout.flush()
	dot_data =  send_message(destination,m,0)
	print len( dot_data )/1024.0,"Kbytes of dot data received"
	sys.stdout.flush()
	return dot_data

def posnet_graph_request(destination,answer_type = None):
	""" send a posnet graph_request message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	"""	
	m  = posnet_overlay_messages.posnet_graph_request_message( (None,None), destination,None)
	print "graph_request to posnet overlay node",destination
	sys.stdout.flush()
	return send_message(destination,m,answer_type)
	#print "on socket",ack_message.target,",",len( ack_message.data ),"bytes of dot data answered from",ack_message.source

def posnet_state_request(destination,answer_type = None):
	""" send a posnet state_request message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	"""	
	m  = posnet_overlay_messages.posnet_state_request_message( (None,None), destination,None)
	print "state_request to posnet overlay node",destination
	sys.stdout.flush()
	return send_message(destination,m,answer_type)

def posnet_point_query(destination,query_name,space_point,source = None,entering_node = None,logger=None):
	""" send a query to a posnet overlay node
	    @param destination: id of the node contacted
	"""
	# Debug
	#####if logger is not None: logger.info("send query to "+str(destination)+" : "+str(query_name))
	#####else: print "send query to",destination
	#-----------
	if source is None: source = (None,None)
	query = posnet_overlay_messages.posnet_semantic_multiquery(source,entering_node,query_name,space_point)
	#print "target for query: ",query.target
	m = posnet_overlay_messages.posnet_query_message(source,destination,query)
	ack_messages = send_query_message(destination,m,logger)
	return ack_messages
	#pprint(ack_messages)


def posnet_add_random_handle(starting_node,Poset = None):
	if Poset is None: Poset = posnet_ping( starting_node)
	Poset_keys = Poset.keys()
	Poset_keys.sort()
	space_point = [random.choice(Poset[y]) for y in Poset_keys]
	handle = ""
	return posnet_add_handle(starting_node,space_point,handle)

def posnet_add_random_handle_range(starting_node,Poset_keys = None,start = None,stop =  None):
	if Poset_keys is None: Poset_keys = posnet_ping( starting_node).keys()
	Poset_keys.sort()
	space_point = [random.randrange(start,stop) for y in Poset_keys]
	handle = ""
	return posnet_add_handle(starting_node,space_point,handle)



def posnet_add_handle(starting_node,space_point,handle):
	if handle is None: handle = ""
	add_handle_message = posnet_overlay_messages.posnet_add_handle(space_point,handle,True)
	message = posnet_overlay_messages.posnet_overlay_add_handle_message(("",0),starting_node,   add_handle_message)
	ack_message = send_tcp_message( starting_node, message,True,True)
	sys.stdout.flush()
	sys.stderr.flush()
	if ack_message is not None: return ack_message.data
	else: return None

def posnet_lock(destination,space_points_and_states):
	""" send a posnet lock message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	    @param space_points_and_states:  	
	"""
	lockdata = posnet_overlay_messages.posnet_lock(space_points_and_states )
	m   = posnet_overlay_messages.posnet_lock_message( (None,None), destination , lockdata)
	print "lock posnet overlay node",destination
	sys.stdout.flush()
	m.answer_type = 0
	ack_message = send_tcp_message(destination,m,True,True)	
	print "ACK on socket",ack_message.target,"reply from",ack_message.source,":",ack_message
	return ack_message.data

def posnet_unlock(destination,space_points_tab,is_cancel):
	""" send a posnet lock message to a posnet overlay node
	    @param destination: id of the destination, this may be (address,port)
	    @param space_points_tab: table of space points to unlock
	    @param is_cancel: if true indicates that the unlock message is a cancel message
	"""
	lockdata = posnet_overlay_messages.posnet_unlock(space_points_tab,is_cancel)
	m   = posnet_overlay_messages.posnet_unlock_message( (None,None), destination , lockdata)
	print "unlock posnet overlay node",destination
	sys.stdout.flush()
	m.answer_type = 0
	ack_message = send_tcp_message(destination,m,True,True)	
	#print "ACK on socket",ack_message.target,"reply from",ack_message.source,":",ack_message
	#return ack_message.data

#----------------------------------------------
# everything about drawing
def draw_dot(out,dot_data,has_pos = None):
	""" draw a dot (graphivz content). This creates a dot file and the png output file.
	    @param out: the base name for files (dot file will be 'out'.dot, png file will be 'out'.png )
	    @param dot_data: dot data without the global graph encapsulation
	"""
	if has_pos is None: has_pos = False
	f = file( out +".dot","w")
	sp = dot_data.split("\n")
	print >>f,"digraph G {"
	for line in sp:
		if line.find("[label=\"") > -1 and line.find("->") == -1:
			tt = line.split("[label=\"")
			t2 = tt[1].split("\"")
			lab = t2[0]
			if len(lab) < 20: 	
				print >>f,line	
			else:
				print >>f,tt[0]+"[label=\"\""+"\"".join(t2[1:])
		else: print >>f,line
	print >>f,"}"
	f.close()
	cmd = "dot -T png "
	if has_pos: cmd +=  " -n "
	cmd += " -o "+out+".png "+out+".dot"
	os.system(cmd)
	print >>sys.stderr,"DRAW:",out+".png"
	sys.stderr.flush()
	#os.system("circo -T png -o "+out+"-circo.png "+out+".dot")
	#os.system("neato -T png -o "+out+"-neato.png "+out+".dot")

def compute_top(G_dict):
	top = []
	for node,node_info in G_dict.items():
		is_top = True
		for neigh,rel in node_info.items():
			if rel == "c2p":
				is_top = False
				break
		if not is_top: continue
		top.append(node)
	return top

def compute_pos(G_dict,algo,top = None):
	if top is None: top = compute_top(G_dict)
	pos =dict()
	if len(G_dict) == 0: return pos
	#------------------
	nodelist = sets.Set()
	h  = dict()  
	for node in G_dict.keys(): h[node] = 0	
	sys.stdout.flush()
	for node in top:
		for pred,rel in G_dict[node].items():
			if rel != "p2c": continue
			if pred in top: continue
			h[pred] = 1
			nodelist.add(pred)
	while len(nodelist) > 0:
		#print "nodelist",nodelist
		sys.stdout.flush()
		nodelist2 = sets.Set()  		
		for node in nodelist:
			d = h[node] +1
			for pred,rel in G_dict[node].items():
				if rel != "p2c": continue
				if h[pred] >= d: continue
				h[pred] = d
				nodelist2.add(pred)	
		nodelist = nodelist2
	#------------------
	by_h = dict()
	for node,l in h.items():
		if not by_h.has_key(l): by_h[l] = []
		by_h[l].append(node)	
	lengths = by_h.keys()
	max_n_t = [ len(by_hval) for by_hval in by_h.values()]
	max_n_t.sort()
	max_n = float(max_n_t[-1])
	lengths.sort()
	max_length = lengths[-1]
	for le in lengths:
		xx = 1 
		yy = max_length-le
		nodelist = by_h[le]
		nodelist.sort() 
		for no in nodelist:
			x_position = xx
			y_position = yy
			if algo == 1: pass
			elif algo == 2: x_position =  float(xx)*float(max_n+1)/float(len(nodelist)+1) 
			elif algo == 3: x_position = float(xx)  + 0.1* float(yy)
			elif algo == 4:
				x_position = float(xx)  + 0.1* float(yy+xx)
				y_position = float(yy) + 0.5 * float(xx)
			elif algo == 5: x_position =  float(xx)*float(max_n+1 + 2 * le)/float(len(nodelist)+1)  - float(le)
			pos[no] = [ x_position,y_position]
			xx += 1
	return pos



def posnet_check(starting_node,base_dir = None,prefix= None):
	complete_graph = dict()
	all_points = sets.Set()
	#----
	Poset = posnet_ping(starting_node)
	from posnet_semantic_graph import posnet_local_semantic_graph
	sg = posnet_local_semantic_graph(Poset) # used to transform points from list to tuples
	#----
	all_nodes = sets.Set()
	visited_nodes = sets.Set()
	all_nodes.add(starting_node) 
	#----
	graph_data = dict() # used to link semantic nodes from different overlay nodes	a
	#----
	if base_dir is None: base_dir = "check"
	if not os.path.exists(base_dir): os.makedirs(base_dir)
	if prefix is None: prefix= ""
	prefix = prefix.split("/")[-1]
	#----
	##f = cStringIO.StringIO()
	##f2 = cStringIO.StringIO()
	while len(all_nodes.difference(visited_nodes)) > 0:
		node = random.choice( [x for x in all_nodes.difference(visited_nodes)] )
		host, port = node 	
		towrite = dict()
		#------------
		boot_data = posnet_draw( (host,port),  False )
		visited_nodes.add(node)
		##picture1 = boot_data["picture"]
		###draw_dot(  os.path.join(base_dir,prefix+"overlay_node_"+str(host)+"_"+str(port)) , picture1)
		##print >>f,picture1
		neighbors = boot_data["neighbors"]
		for n in neighbors: all_nodes.add(n)
		ext  = boot_data["ext"]
		mygr = boot_data["graph"]
		#-- 1. verify consistency between neigbors and ext		
		neighbors_from_ext = sets.Set()
		for ext_link in ext:
			label_src,space_point_dst,overlay_node_dst,relation = ext_link
			neighbors_from_ext.add( overlay_node_dst )
		neighbors_set = sets.Set()
		for n in neighbors: neighbors_set.add(n)
		diff_set_ext = neighbors_set.difference(neighbors_from_ext)
		diff_ext_set = neighbors_from_ext.difference(neighbors_from_ext)
		if len(diff_set_ext) > 0:
			print node,": in neighbors but not in ext:",diff_set_ext
		if len(diff_ext_set) > 0:
			print node,": in ext but not in neighbors:",diff_ext_set
		for kk,vv in mygr.items():
			tab = []
			all_points.add( kk )
			if not complete_graph.has_key(kk):  complete_graph[kk] = dict()
			for kkk,vvv in vv.items(): complete_graph[kk][kkk] = vvv
		continue
		#------------
		# draw external nodes
		for ext_link in ext:
			label_src,space_point_dst,overlay_node_dst,relation = ext_link
			if not  graph_data.has_key( overlay_node_dst ): graph_data[ overlay_node_dst ] = posnet_graph_request(overlay_node_dst)	
			try:
				myhash = sg.hashable_space_point(space_point_dst)
				vertex_id_dst = graph_data[overlay_node_dst]["vertex_id"][myhash]
				label_dst =   ""+"".join([str(x) for x in space_point_dst])+""
				if relation != 'c2p':
					key =  ( node,overlay_node_dst, relation)
					if towrite.has_key(key): towrite[key] += 1
					else: towrite[key] = 1
				if relation != "p2c" : continue
				print >>f, label_src,"->",label_dst,"[label=\""+relation+"\",color=black,style=bold];"
			except Exception,e: 
				print "EXCEPTION",e,label_src,space_point_dst,overlay_node_dst,relation
				raise
		#draw external links
		for key,number in towrite.items():	
			x,y,relation = key
			x = str(x[0]).replace("-","").replace(":","_")+"__"+str(x[1])
			y = str(y[0]).replace("-","").replace(":","_")+"__"+str(y[1])
			if number != 1:
				print >>f2,x,"->",y ,"[label=\""+relation+"("+str(number)+")\",color=black];"
			else: print >>f2,x,"->",y ,"[label=\""+relation+"\",color=black];"
		for x in all_nodes:
			x = str(x[0]).replace("-","").replace(":","_")+"__"+str(x[1])
			print >>f2,x,";"
	return
	# draw the two topologies		
	draw_dot( os.path.join(base_dir,prefix+"overlay"), f2.getvalue())
	draw_dot( os.path.join(base_dir,prefix+"semantic_graph"), f.getvalue())
	f.close()
	f2.close()
	top = compute_top(complete_graph)
	for algo in [5]: #[2,3,4,5]:
		positions  = compute_pos(complete_graph,algo,top)
		f3 = cStringIO.StringIO()
		def elemstr(el,j = None,rep = None):
			if j is None: j = ""
			if rep is None: rep = True
			if rep:  return j.join( [str(xx).replace("-","") for xx in el] )
			else:	return j.join( [str(xx)  for xx in el] )
		for element in complete_graph.keys():
			coord = positions[element]
			print >>f3,elemstr(element),'[label="'+str(elemstr(element," ",False))+'", pos="'+str(coord[0])+","+str(coord[1])+'",',
			if element not in top:
				print >>f3,'shape="circle",style=filled',
			else:
				print >>f3,'shape="square",color=green',
			print >>f3,']'
		for el,el_data in complete_graph.items():
			for neig,rel in el_data.items():
				if rel !="p2c": continue
				print >>f3,elemstr(el),"->",elemstr(neig),'[label="",color=black]'
		draw_dot( os.path.join(base_dir,prefix+"semantic_graph_algo"+str(algo)), f3.getvalue(),True)
		f3.close()
	fff= file(os.path.join(base_dir,prefix+"points.tsv"),"w")
	for one_point in all_points:
		print >>fff,"\t".join([str(x) for x in one_point])
	fff.close()

def posnet_draw_node(starting_node,base_dir = None,prefix= None,Poset = None):
	""" draw node topology. create dot and png files in the output directory specified
	    @param starting_node: the starting point for queries (address,port)
	    @param base_dir: output directory for dot and png files (default: current directory)	
	    @param prefix: output prefix for filename (default: "") 
	"""
	complete_graph = dict()
	all_points = sets.Set()
	#----
	if Poset is None: Poset = posnet_ping( starting_node)
	from posnet_semantic_graph import posnet_local_semantic_graph
	sg = posnet_local_semantic_graph(Poset) # used to transform points from list to tuples
	#----
	all_nodes = sets.Set()
	visited_nodes = sets.Set()
	all_nodes.add(starting_node ) 
	#----
	graph_data = dict() # used to link semantic nodes from different overlay nodes	a
	#----
	if base_dir is None: base_dir = ""
	if not os.path.exists(base_dir): os.makedirs(base_dir)
	if prefix is None: prefix= ""
	prefix = prefix.split("/")[-1]
	#----
	f = cStringIO.StringIO()
	while len(all_nodes.difference(visited_nodes)) > 0:
		node = random.choice( [x for x in all_nodes.difference(visited_nodes)] )
		host, port = node 	
		towrite = dict()
		#------------
		boot_data = posnet_draw( (host,port),  False )
		visited_nodes.add(node)
		picture1 = boot_data["picture"]
		#draw_dot(  os.path.join(base_dir,prefix+"overlay_node_"+str(host)+"_"+str(port)) , picture1)
		print >>f,picture1
		neighbors = boot_data["neighbors"]
		#for n in neighbors: all_nodes.add(n)
		ext  = boot_data["ext"]
		mygr = boot_data["graph"]	
		for kk,vv in mygr.items():
			tab = []
			all_points.add( kk )
			if not complete_graph.has_key(kk):  complete_graph[kk] = vv	
			else:
				for kkk,vvv in vv.items(): complete_graph[kk][kkk] = vvv
		#------------
		# draw external nodes
		for ext_link in ext:
			label_src,space_point_dst,overlay_node_dst,relation = ext_link
			if not  graph_data.has_key( overlay_node_dst ): graph_data[ overlay_node_dst ] = posnet_graph_request(overlay_node_dst)	
			try:
				myhash = sg.hashable_space_point(space_point_dst)
				vertex_id_dst = graph_data[overlay_node_dst]["vertex_id"][myhash]
				label_dst =   ""+"".join([str(x) for x in space_point_dst])+""
				if relation != 'c2p':
					key =  ( node,overlay_node_dst, relation)
					if towrite.has_key(key): towrite[key] += 1
					else: towrite[key] = 1
				if relation != "p2c" : continue
				print >>f, label_src,"->",label_dst,"[label=\""+relation+"\",color=black,style=bold];"
			except Exception,e: 
				print "EXCEPTION",e,label_src,space_point_dst,overlay_node_dst,relation
				raise
		#draw external links
		for key,number in towrite.items():	
			x,y,relation = key
			x = str(x[0])+"_"+str(x[1])
			y = str(y[0])+"_"+str(y[1])
			#if number != 1:
			#	print >>f2,x,"->",y ,"[label=\""+relation+"("+str(number)+")\",color=black];"
			#else: print >>f2,x,"->",y ,"[label=\""+relation+"\",color=black];"
		#for x in all_nodes:
		#	x = str(x[0])+"_"+str(x[1])
		#	print >>f2,x,";"
	# draw the two topologies
	filename = prefix+"semantic_graph_node"+str(starting_node[0])+"_"+str(starting_node[1])
	draw_dot( os.path.join(base_dir,filename), f.getvalue())
	f.close()
	fff= file(os.path.join(base_dir,prefix+"points_node"+str(starting_node[0])+"_"+str(starting_node[1])+".tsv"),"w")
	for one_point in all_points:
		print >>fff,"\t".join([str(x) for x in one_point])
	fff.close()
	return os.path.join(base_dir,filename+".png")



def posnet_draw_topology(starting_node,base_dir = None,prefix= None,Poset = None,draw_algorithm = None):
	""" draw posnet topology. create dot and png files in the output directory specified
	    @param starting_node: the starting point for queries (address,port)
	    @param base_dir: output directory for dot and png files (default: current directory)	
	    @param prefix: output prefix for filename (default: "") 
	"""
	complete_graph = dict()
	all_points = sets.Set()
	#----
	if Poset is None: Poset = posnet_ping( starting_node)
	from posnet_semantic_graph import posnet_local_semantic_graph
	sg = posnet_local_semantic_graph(Poset) # used to transform points from list to tuples
	#----
	all_nodes = sets.Set()
	visited_nodes = sets.Set()
	all_nodes.add(starting_node ) 
	#----
	graph_data = dict() # used to link semantic nodes from different overlay nodes	a
	#----
	if base_dir is None: base_dir = ""
	if not os.path.exists(base_dir): os.makedirs(base_dir)
	if prefix is None: prefix= ""
	prefix = prefix.split("/")[-1]
	#----
	f = cStringIO.StringIO()
	f2 = cStringIO.StringIO()
	while len(all_nodes.difference(visited_nodes)) > 0:
		node = random.choice( [x for x in all_nodes.difference(visited_nodes)] )
		host, port = node 	
		towrite = dict()
		#------------
		boot_data = posnet_draw( (host,port),  False )
		visited_nodes.add(node)
		picture1 = boot_data["picture"]
		#draw_dot(  os.path.join(base_dir,prefix+"overlay_node_"+str(host)+"_"+str(port)) , picture1)
		print >>f,picture1
		neighbors = boot_data["neighbors"]
		for n in neighbors: all_nodes.add(n)
		ext  = boot_data["ext"]
		mygr = boot_data["graph"]	
		for kk,vv in mygr.items():
			tab = []
			all_points.add( kk )
			if not complete_graph.has_key(kk):  complete_graph[kk] = vv	
			else:
				for kkk,vvv in vv.items(): complete_graph[kk][kkk] = vvv
		#------------
		# draw external nodes
		for ext_link in ext:
			label_src,space_point_dst,overlay_node_dst,relation = ext_link
			if not  graph_data.has_key( overlay_node_dst ): graph_data[ overlay_node_dst ] = posnet_graph_request(overlay_node_dst)	
			try:
				myhash = sg.hashable_space_point(space_point_dst)
				vertex_id_dst = graph_data[overlay_node_dst]["vertex_id"][myhash]
				label_dst =   ""+"".join([str(x) for x in space_point_dst])+""
				if relation != 'c2p':
					key =  ( node,overlay_node_dst, relation)
					if towrite.has_key(key): towrite[key] += 1
					else: towrite[key] = 1
				if relation != "p2c" : continue
				print >>f, label_src,"->",label_dst,"[label=\""+relation+"\",color=black,style=bold];"
			except Exception,e: 
				print "EXCEPTION",e,label_src,space_point_dst,overlay_node_dst,relation
				raise
		#draw external links
		def mygetnodeid(s):
			for c in (":",";",".","_"):
				s = s.replace(c,"000")
			return s
		for key,number in towrite.items():	
			x,y,relation = key
			x = str(x[0])+"_"+str(x[1])
			y = str(y[0])+"_"+str(y[1])
			label_edge = relation
			if number != 1: label_edge += "("+str(number)+")"
			print >>f2,mygetnodeid(x),"->",mygetnodeid(y),"[label=\""+label_edge+"\",color=black];"
		for x in all_nodes:
			x = str(x[0])+"_"+str(x[1])
			print >>f2,mygetnodeid(x)," [label=\""+str(x)+"\"];"
	# draw the two topologies		
	draw_dot( os.path.join(base_dir,prefix+"overlay"), f2.getvalue())
	draw_dot( os.path.join(base_dir,prefix+"semantic_graph"), f.getvalue())
	f.close()
	f2.close()
	top = compute_top(complete_graph)
	if draw_algorithm is None: draw_algorithm = 5
	for algo in [draw_algorithm]: #[2,3,4,5]:
		positions  = compute_pos(complete_graph,algo,top)
		f3 = cStringIO.StringIO()
		def elemstr(el,j = None):
			if j is None: j = ""
			return j.join( [str(xx).replace("-","") for xx in el] )
		for element in complete_graph.keys():
			coord = positions[element]
			lab = elemstr(element,"_")
			print "label",element,lab
			print >>f3,elemstr(element),'[label="'+lab+'", pos="'+str(coord[0])+","+str(coord[1])+'",',
			if element not in top:
				print >>f3,'shape="circle",style=filled',
			else:
				print >>f3,'shape="square",color=green',
			print >>f3,']'
		for el,el_data in complete_graph.items():
			for neig,rel in el_data.items():
				if rel !="p2c": continue
				print >>f3,elemstr(el),"->",elemstr(neig),'[color=black]'
		draw_dot( os.path.join(base_dir,prefix+"semantic_graph_algo"+str(algo)), f3.getvalue(),True)
		f3.close()
	fff= file(os.path.join(base_dir,prefix+"points.tsv"),"w")
	for one_point in all_points:
		print >>fff,"\t".join([str(x) for x in one_point])
	fff.close()
	return all_nodes

if __name__  == "__main__":
	import sys
	host = sys.argv[1] # server address
	port = int(sys.argv[2]) # server port

	mydata = ""
	if len(sys.argv) > 3: mydata = sys.argv[3]
	if mydata == "quit": 
		send_quit_tcp_message(host,port)
	elif mydata == "check":
		posnet_check( (host,port))
	elif mydata in ("bulkdebug","bulk_and_draw","bulk"):
		Poset = posnet_ping( (host,port))
		if mydata == "bulkdebug":
			keys = Poset.keys()
			for k  in  keys:
				Poset[k] = range(1,10)
		nb = int(sys.argv[4])
		for i in range(nb): 
			posnet_add_random_handle( (host,port), Poset )
			if mydata == "bulk_and_draw": posnet_draw_topology((host,port),"out/",str(i))
	elif mydata in ("bulkrange"):
		Poset = posnet_ping( (host,port))
		nb = int(sys.argv[4])
		for i in range(nb):
			posnet_add_random_handle_range( (host,port), Poset.keys(), 1,10000)
	elif mydata in ("load"):
		fff = file(sys.argv[4],"r")
		line = fff.readline()
		while len(line) > 0:
			sp = [int(y) for y in line[:-1].split("\t")]
			posnet_add_handle( (host,port), sp , '')
			line = fff.readline()
	#--------------------------------------------------------------
	elif mydata == "ping":
		Poset = posnet_ping( (host,port), 0)
		pprint(Poset)
	elif mydata == "ping2":
		Poset = posnet_ping( (host,port), 1)
		pprint(Poset)
	elif mydata == "graph":
		dat = posnet_graph_request(  (host,port), 0 )
		pprint(dat)
	elif mydata == "graph2":
		dat = posnet_graph_request(  (host,port), 1 )
		pprint(dat)
	elif mydata == "state":
		dat = posnet_state_request(  (host,port), 0 )
		pprint(dat)
	elif mydata == "state2":
		dat = posnet_state_request(  (host,port), 1 )
		pprint(dat)
	elif mydata == "dot":
		res = posnet_draw((host,port))
		pprint(res)
	elif mydata == "draw2":
		outdir = "out"
		outname = ""
		if len(sys.argv) > 4: outdir = sys.argv[4]
		if len(sys.argv) > 5: outname = sys.argv[5]
		posnet_draw_node((host,port),outdir+"/",outname)
	elif mydata == "draw":
		outdir = "out"
		outname = ""
		if len(sys.argv) > 4: outdir = sys.argv[4]
		if len(sys.argv) > 5: outname = sys.argv[5]
		posnet_draw_topology((host,port),outdir+"/",outname)
	#-------------------------------------------------------------
	elif mydata == "query":
		Poset = posnet_ping( (host,port), 0)
		point = dict()
		keys = Poset.keys()
		i = 0
		for k  in  keys:
			point[k[-1]] = int( sys.argv[4+i])
			i += 1
		results = posnet_point_query( (host,port), "exact (Point query)", point )
		print "point query result:"
		sys.stdout.flush()
		for result in results:
				print result.data
	elif mydata in ("exact", "all_parents", "all_top", "all_children" ):
		Poset = posnet_ping( (host,port), 0)
		point = dict()
		keys = Poset.keys()
		keys.sort()
		i = 0
		for k  in  keys:
			if len(sys.argv) > 4+i:
				point[k[-1]] = int( sys.argv[4+i])
			else:
				point[k[-1]] = random.randrange(1,10)
			i += 1
		query_name = mydata+" (Point query)"
		print query_name,"(",point,")"
		results = posnet_point_query( (host,port), query_name, point )
		print "result:"
		sys.stdout.flush()
		for result in results:
				print result.data
	elif mydata == "rquery":
		Poset = posnet_ping( (host,port))
		point = dict()
		keys = Poset.keys()
		for k  in  keys:
			point[k[-1]] =  random.randrange(1,10)
		print "point query :",point
		results = posnet_point_query( (host,port), "all_parents (Point query)", point )
		print "point query result:"
		sys.stdout.flush()
		for result in results:
				print result.data

	elif mydata == "parents":
		x = 2
		y = 2
		if len(sys.argv) > 4: x = sys.argv[4]
		if len(sys.argv) > 5: y = sys.argv[5] 
		results = posnet_point_query( (host,port), "all_parents (Point query)", {"x":x,"y":y})
		print "all_parents point query result:"
		sys.stdout.flush()
		for result in results:
				print result.data
	elif mydata == "add":
		x = 2
		y = 2
		if len(sys.argv) > 4: x = int(sys.argv[4])
		if len(sys.argv) > 5: y = int(sys.argv[5] )
		posnet_add_handle( (host,port), [x,y] ,"")
	elif mydata == "add2":
		posnet_add_handle( (host,port), [8,4] ,"")
	elif mydata == "add3":
		posnet_add_handle( (host,port), [4,6] ,"")
	elif mydata == "lock":
		results = posnet_lock( (host,port) , {  (2,2)  : 0 } )
		print "lock results:"
		lock_reply = results
		print lock_reply
	elif mydata == "unlock":
		results = posnet_unlock( (host,port) , [(2,2)] , 1)
		print "unlock results:"
		lock_reply = results
		print lock_reply
	elif mydata == "unlock.cancel":
		results = posnet_unlock( (host,port) , [(2,2)] , 0)
		print "unlock results:"
		lock_reply = results
		print lock_reply
	else:
		raise SystemExit
		#print posnet_point_query( (host,port), "exact (Point query)", {"X":8,"Y":6})			
		#print posnet_point_query( (host,port), "all_top (Point query)", [5,1])			
		#results =  posnet_point_query( (host,port), "all_fit (Point query)", [1,1])			
		#for result in results:
		#	print result.data
		posnet_draw_topology((host,port),"out/",str(0)+"_")
		Poset = posnet_ping( (host,port))
		keys = Poset.keys()
		for k  in  keys:
			Poset[k] = range(1,10)
		for i in range(5): 
			posnet_add_random_handle( (host,port), Poset )
			posnet_draw_topology((host,port),"out/",str(i+1)+"_")	
	#---------------------------------------------------

